/*
 * ArchivoTexto.cpp
 *
 *  Created on: 17/10/2009
 *      Author: ngonzalez
 */

#include "ArchivoTexto.h"


ArchivoTexto::ArchivoTexto(const std::string& path) {
	//intenta abrir el archivo en modo lectura - escritura
	archivo.open(path.c_str(), std::fstream::in | std::fstream::out);
	//  archivo.open(path.c_str(), std::ios::ate | std::ios::out | std::ios::in);


	if (!archivo.is_open()) {
		//si no hubo éxito en la apertura...
		//limpia los flags de control de estado del archivo
		archivo.clear();

		//crea el archivo
		archivo.open(path.c_str(), std::fstream::out);
		archivo.close();

		//reabre el archivo para lectura - escritura
		archivo.open(path.c_str(), std::fstream::in | std::fstream::out);

		archivo << 0;
		archivo << endl;

		if (!archivo.is_open())
			// si no se pudo crear el archivo arroja una excepción/
			throw std::ios_base::failure("El archivo no pudo ser creado / abierto");
	}
	this->setPath(path);
}

ArchivoTexto::~ArchivoTexto() {
  archivo.close();
}

void ArchivoTexto::create(const string& nombre){
	//intenta abrir el archivo en modo lectura - escritura
	archivo.open(path.c_str(), std::fstream::in | std::fstream::out);
	//  archivo.open(path.c_str(), std::ios::ate | std::ios::out | std::ios::in);


	if (!archivo.is_open()) {
		//si no hubo éxito en la apertura...
		//limpia los flags de control de estado del archivo
		archivo.clear();

		//crea el archivo
		archivo.open(path.c_str(), std::fstream::out);
		archivo.close();

		//reabre el archivo para lectura - escritura
		archivo.open(path.c_str(), std::fstream::in | std::fstream::out);

		archivo << 0;
		archivo << endl;

		if (!archivo.is_open())
		  // si no se pudo crear el archivo arroja una excepción/
		  throw std::ios_base::failure("El archivo no pudo ser creado / abierto");
	}
	this->setPath(path);
}



string ArchivoTexto::getPath() const{
	return ArchivoTexto::path;
}

void ArchivoTexto::setPath(string path){
	this->path = path;
}


void ArchivoTexto::escribir(char c) {
  // Intenta escribir el caracter en el archivo
  archivo.put(c);

  if (archivo.fail())
    //si se produjo un error, arroja una excepción
    throw std::ios_base::failure("No se pudo escribir correctamente el registro");
}

void ArchivoTexto::escribir(const std::string& cadena) {
  //intenta escribir la cadena en el archivo
   archivo << cadena;

  if (archivo.fail())
    // si se produjo un error, arroja una excepción
    throw std::ios_base::failure("No se pudo escribir correctamente la cadena");
}

void ArchivoTexto::terminarLinea() {
  // intenta escribir en el archivo
  archivo << endl;

  if (archivo.fail())
    //si se produjo un error, arroja una excepción
    throw std::ios_base::failure("No se pudo terminar la linea");
}

void ArchivoTexto::tabular() {
  escribir('\t');
}

bool ArchivoTexto::leerLinea(std::string &cadena) {
  char linea[MAX_LENGTH];

  // lee del archivo a la linea, hasta haber leido:
  // MAX_LENGTH caracteres, o un fin de linea
  archivo.getline( (char*)&linea , MAX_LENGTH , '\n');
  cadena = linea;

  if (archivo.fail() ) {
    //chequea si se ha producido un error, se devuelve false
    archivo.clear();
    return false;
  }
  return true;
}


string ArchivoTexto::leerLinea() {
  char linea[MAX_LENGTH];
  archivo.getline( (char*)&linea , MAX_LENGTH , '\n');
  return linea;
}



/**
 * Posiciona el cursor al comienzo del archivo
 */
void ArchivoTexto::irAlPrincipio() {
  archivo.tellg();
  archivo.clear();
  archivo.seekg(0);
  archivo.seekp(0);
  archivo.tellg();
}

/**
 * Posiciona el cursor al final del archivo
 */
void ArchivoTexto::irAlFinal() {
  archivo.tellg();
  archivo.clear();
  archivo.seekg(0, ios::end);
  archivo.seekp(0, ios::end);
  archivo.tellg();
}

string ArchivoTexto::getTextFileSize(){
	if (archivo.is_open()){

		archivo.seekg(-10, ios::end);

		std::string line;
		int counter = 1;
		string final = "";

		// For each line...
		while (std::getline(archivo, line)) {
		    std::istringstream iss(line);
		    string c;


		    if (counter == 0){
				while (iss >> c) {
				   final = final + c;
				}
			}
		    counter--;
		}
		return final;
	}
	return 0;
}


void ArchivoTexto::seekToOverrideFileSize(int offset){
	this->archivo.seekp(offset,ios::end);
}

void ArchivoTexto::updateTextFileSize(int olderFileSize){
	int updatedFileSize = olderFileSize + 1;
	string s;
	stringstream out;
	out << updatedFileSize;
	s = out.str();
	this->escribir(s);
}


/*Fue pensado por ahora para la estegia de almacenamiento de texto, no usar para otra cosa*/
int ArchivoTexto::saveData(Dato *data){
	string fileSize;

	if (archivo.is_open()){
    	fileSize = this->getTextFileSize();
    	int offset = fileSize == "" ? -2 : ((fileSize.length())*-1)-1;
    	this->irAlFinal();
    	this->seekToOverrideFileSize(offset);
		this->escribir(data->serializar());
		this->terminarLinea();
		this->irAlFinal();
		this->updateTextFileSize(atoi(fileSize.c_str()));
        this->terminarLinea();
        return atoi(fileSize.c_str());
    }
	else{
		throw std::ios_base::failure("[ArchivoTexto::saveData] : Se produjo un error al intetar guardar informacion en el archivo");
	}
}

int ArchivoTexto::saveData(string data, int posicion){
	string fileSize;

	if (archivo.is_open()){
    	fileSize = this->getTextFileSize();
    	int offset = fileSize == "" ? -2 : ((fileSize.length())*-1)-1;
    	this->irAlFinal();
    	this->seekToOverrideFileSize(offset);
		this->escribir(data);
		this->terminarLinea();
		this->irAlFinal();
		this->updateTextFileSize(atoi(fileSize.c_str()));
        this->terminarLinea();
        return atoi(fileSize.c_str());
    }
	else{
		throw std::ios_base::failure("[ArchivoTexto::saveData] : Se produjo un error al intetar guardar informacion en el archivo");
	}
}

int ArchivoTexto::saveData(string data){
string fileSize;

	if (archivo.is_open()){
		fileSize = this->getTextFileSize();
		int offset = fileSize == "" ? -2 : ((fileSize.length())*-1)-1;
		this->irAlFinal();
		this->seekToOverrideFileSize(offset);
		this->escribir(data);
		this->terminarLinea();
		this->irAlFinal();
		this->updateTextFileSize(atoi(fileSize.c_str()));
		this->terminarLinea();
		return atoi(fileSize.c_str());
	}
	else{
		throw std::ios_base::failure("[ArchivoTexto::saveData] : Se produjo un error al intetar guardar informacion en el archivo");
	}
}



void ArchivoTexto::buildOverwritingLineLength(string line, string & myData) {

	int firstPositionRecordToOverride = archivo.tellg();

    std::getline(this->archivo, line);

    int lastPositionRecordToOverride = archivo.tellg();

    int recordToOverrideSize = lastPositionRecordToOverride - firstPositionRecordToOverride;

    int theLenght = (int)(myData.length());

    if(recordToOverrideSize > theLenght){

        int difference = recordToOverrideSize - myData.length();

        while(difference > 1){
            myData.append(" ");
            difference--;
        }
    }
    this->archivo.seekp(firstPositionRecordToOverride, ios::beg);
}

void ArchivoTexto::updateData(int position, Dato *data){

	string line;

	ofstream out("auxFile.txt");

	if( !this->archivo.is_open()){
		throw std::ios_base::failure("[ArchivoTexto::updateData] : El archivo no está abierto");
	}

	int lineNumberCounter = 0;

	bool found = false;

	this->irAlPrincipio();

	while(std::getline(this->archivo,line) ) {


		if (lineNumberCounter != position){
			out << line << "\n";
		} else{
			out << data->serializar() << "\n";
			found = true;
		}

		lineNumberCounter ++;
	}

	if (!found){
		this->irAlPrincipio();
		remove("auxFile.txt");
		out.close();
		throw std::ios_base::failure("[ArchivoTexto::updateData] : La posicion indicada es incorrecta o supera al tamaño del archivo");
	}

	//Borro el archivo Original
	remove(this->getPath().c_str());
	rename("auxFile.txt",this->getPath().c_str());

	this->close();
	out.close();

}

void ArchivoTexto::updateData(int position, string data){

	string line;

	ofstream out("auxFile.txt");

	if( !this->archivo.is_open()){
		throw std::ios_base::failure("[ArchivoTexto::updateData] : El archivo no está abierto");
	}

	int lineNumberCounter = 0;

	bool found = false;

	this->irAlPrincipio();

	while(std::getline(this->archivo,line) ) {



		if (lineNumberCounter != position){
			out << line << "\n";
		} else{
			out << data << "\n";
			found = true;
		}

		lineNumberCounter ++;
	}

	if (!found){
		this->irAlPrincipio();
		remove("auxFile.txt");
		out.close();
		throw std::ios_base::failure("[ArchivoTexto::updateData] : La posicion indicada es incorrecta o supera al tamaño del archivo");
	}

	//Borro el archivo Original
	remove(this->getPath().c_str());
	rename("auxFile.txt",this->getPath().c_str());

	this->close();
	out.close();

}




/**
 * Metodo que borra un registro (linea , linea = espacio hasta encontrar un /n) de un archivo de texto.
 * En esta version del metodo, solo se lo reemplaza por una linea en blanco.
 */
void ArchivoTexto::deleteData(int position){
	string line;

	string blank = " ";

	bool found = false;

	this->irAlPrincipio();

	int lineCounter = 0;

	int firstPos;

	while( ((!this->archivo.eof()) ) &&  (!found) ) {

		firstPos = this->archivo.tellg();

		std::getline(this->archivo,line);

		if (lineCounter == position){
		   found = true;
		}
		lineCounter++;
	}

	if (found){
		buildOverwritingLineLength(line, blank);
		this->archivo.seekp(firstPos, ios::beg);
		this->escribir(blank);
		this->terminarLinea();
	}
	else
	    throw std::ios_base::failure("[ArchivoTexto::deleteData] : El numero de linea especificado a borrar es invalido o supera la longitud del archivio");
}

string ArchivoTexto::getData(int position){
	string line;

	int lineCounter = 0;

	this->irAlPrincipio();

	while (!this->archivo.eof()) {
		lineCounter++;

		std::getline(this->archivo,line);

		if (lineCounter == position){
			return line;
		}
	}

	cout << "No se encontro la linea especificada" << endl;
	return " ";
}

string ArchivoTexto::getData(){
	return this->leerLinea();
}


void ArchivoTexto::SetPosicion(long int pos) {}


void ArchivoTexto::clean() {

	if (archivo.is_open()) {
		archivo.close();
	}

	archivo.clear();

	archivo.open(path.c_str(), std::fstream::out);

	if (!archivo.is_open()) {
		cout << "ERROR AL VACIAR ARCHIVO DE TEXTO" << endl;
	}
}

bool ArchivoTexto::isEoS(){
	return ((archivo.fail()) || (this->archivo.eof()));
}


void ArchivoTexto::firstPosition(){
	this->irAlPrincipio();
}


void ArchivoTexto::deleteCurrent(int position){
	this->archivo.seekp(position, ios::beg);

	string line;

	std::getline(this->archivo,line);

	string blank = "";

	buildOverwritingLineLength(line,blank);

	this->archivo.seekp(position, ios::beg);

	this->escribir(blank);

	this->terminarLinea();
}

int ArchivoTexto::getPosition(){
	return this->archivo.tellg();
}


int ArchivoTexto::getOldData(){

	return -2;
}
